# Common sh(1) procedures to be used in the Performance Co-Pilot
# PMDA Install and Remove scripts
#
# $Id: pmdaproc.sh,v 2.5 1997/04/02 18:05:32 kenmcd Exp $
#
# Copyright 1995, Silicon Graphics, Inc.
# ALL RIGHTS RESERVED
# 
# UNPUBLISHED -- Rights reserved under the copyright laws of the United
# States.   Use of a copyright notice is precautionary only and does not
# imply publication or disclosure.
# 
# U.S. GOVERNMENT RESTRICTED RIGHTS LEGEND:
# Use, duplication or disclosure by the Government is subject to restrictions
# as set forth in FAR 52.227.19(c)(2) or subparagraph (c)(1)(ii) of the Rights
# in Technical Data and Computer Software clause at DFARS 252.227-7013 and/or
# in similar or successor clauses in the FAR, or the DOD or NASA FAR
# Supplement.  Contractor/manufacturer is Silicon Graphics, Inc.,
# 2011 N. Shoreline Blvd. Mountain View, CA 94039-7311.
# 
# THE CONTENT OF THIS WORK CONTAINS CONFIDENTIAL AND PROPRIETARY
# INFORMATION OF SILICON GRAPHICS, INC. ANY DUPLICATION, MODIFICATION,
# DISTRIBUTION, OR DISCLOSURE IN ANY FORM, IN WHOLE, OR IN PART, IS STRICTLY
# PROHIBITED WITHOUT THE PRIOR EXPRESS WRITTEN PERMISSION OF SILICON
# GRAPHICS, INC.

# some useful common variables for Install/Remove scripts
#
# writeable root of PMNS
NAMESPACE=${PMNS_DEFAULT-/var/pcp/pmns/root}
PMNSROOT=`basename $NAMESPACE`

# put you PMNS files here
PMNSDIR=`dirname $NAMESPACE`

# install your DSO PMDAs here
DSOPMDADIR=/var/pcp/lib

# Install control variables
#	Can install as DSO?
dso_opt=false
#	Can install as daemon?
daemon_opt=true
#	If daemon, pipe?
pipe_opt=true
#	If daemon, socket?  and default for Internet sockets?
socket_opt=false
socket_inet_def=''
#	IPC Protocol for daemon (binary or text)
ipc_prot=binary
#	Delay after install before checking (sec)
check_delay=3
#	Additional command line args to go in /etc/pmcd.conf
args=""
#	Source for the pmns
pmns_source=pmns
#	Source for the helptext
help_source=help
#	Assume no valid licenses
LICENSE_PCP=false
LICENSE_WEBMETER=false

# Other variables and constants
#
prog=`basename $0`
here=`pwd`
tmp=/var/tmp/$$
do_pmda=true
do_check=true

trap "rm -f $tmp $tmp.*; exit" 0 1 2 3 15

# Parse command line args
#
while [ $# -gt 0 ]
do
    case $1
    in
	-N)	# name space only
	    do_pmda=false
	    ;;

	-n)	# alternate name space
	    if [ $# -lt 2 ]
	    then
		echo "$prog: -n requires a name space file option"
		exit 1
	    fi
	    NAMESPACE=$2
	    PMNSROOT=`basename $NAMESPACE`
	    PMNSDIR=`dirname $NAMESPACE`
	    shift
	    ;;

	-Q)	# skip check for metrics going away
	    do_check=false
	    ;;

	-R)	# $ROOT
	    if [ "$prog" = "Remove" ]
	    then
		echo "Usage: $prog [-N] [-n namespace] [-Q]"
		exit 1
	    fi
	    if [ $# -lt 2 ]
	    then
		echo "$prog: -R requires a directory option"
		exit 1
	    fi
	    root=$2
	    shift
	    ;;

	*)
	    if [ "$prog" = "Install" ]
	    then
		echo "Usage: $prog [-N] [-n namespace] [-Q] [-R rootdir]"
	    else
		echo "Usage: $prog [-N] [-n namespace] [-Q]"
	    fi
	    exit 1
	    ;;
    esac
    shift
done

# pmda_cull name domain
#
pmda_cull()
{
    # context and integrity checks
    #
    [ ! -f /etc/pmcd.conf ] && return
    if [ ! -w /etc/pmcd.conf ]
    then
	echo "pmda_cull: \"/etc/pmcd.conf\" is not writeable"
	exit 1
    fi
    if [ $# -ne 2 ]
    then
	echo "internal botch: pmda_cull() called with $# (instead of 2) arguments"
	exit 1
    fi

    # remove matching entry from /etc/pmcd.conf if present
    #
    nawk </etc/pmcd.conf >/tmp/$$.pmcd.conf '
BEGIN					{ status = 0 }
$1 == "'"$1"'" && $2 == "'"$2"'" 	{ status = 1; next }
					{ print }
END					{ exit status }'
    if [ $? -eq 0 ]
    then
	# no match
	rm -f /tmp/$$.pmcd.conf
	return
    fi
    
    # log change to /var/adm/pcplog/NOTICES
    #
    /usr/pcp/bin/pmpost "pmda_cull: from /etc/pmcd.conf: $1 $2"

    # install updated /etc/pmcd.conf
    #
    cp /tmp/$$.pmcd.conf /etc/pmcd.conf
    rm -f /tmp/$$.pmcd.conf

    # signal pmcd if it is running
    #
    if pminfo -v pmcd.version >/dev/null 2>&1
    then
	killall -HUP pmcd
	# wait for pmcd to respond ...
	# would be better to have a pmcd generation number, but we do not
	# have that
	sleep 10
    fi
}

# pmda_add "entry for /etc/pmcd.conf"
#
pmda_add()
{
    # context and integrity checks
    #
    if [ ! -w /etc/pmcd.conf ]
    then
	echo "pmda_add: \"/etc/pmcd.conf\" is not writeable"
	exit 1
    fi
    if [ $# -ne 1 ]
    then
	echo "internal botch: pmda_add() called with $# (instead of 1) arguments"
	exit 1
    fi

    myname=`echo $1 | nawk '{print $1}'`
    mydomain=`echo $1 | nawk '{print $2}'`
    # add entry to /etc/pmcd.conf
    #
    echo >/tmp/$$.pmcd.access
    nawk </etc/pmcd.conf '
NF==0					{ next }
/^[      ]*\[[   ]*access[       ]*\]/	{ state = 2 }
state == 2				{ print >"'/tmp/$$.pmcd.access'"; next }
$1=="'$myname'" && $2=="'$mydomain'"	{ next }
					{ print >"'/tmp/$$.pmcd.body'"; next }'
    ( cat /tmp/$$.pmcd.body \
      ; echo "$1" \
      ; cat /tmp/$$.pmcd.access \
    ) >/etc/pmcd.conf
    rm -f /tmp/$$.pmcd.access /tmp/$$.pmcd.body

    # log change to /var/adm/pcplog/NOTICES
    #
    /usr/pcp/bin/pmpost "pmda_add: to /etc/pmcd.conf: $1"

    # signal pmcd if it is running, else start it
    #
    if pminfo -v pmcd.version >/dev/null 2>&1
    then
	killall -HUP pmcd
	# wait for pmcd to respond ...
	# would be better to have a pmcd generation number, but we do not
	# have that
	sleep 10
    else
	log=${PCP_LOGDIR-/var/adm/pcplog}/pmcd.log
	rm -f $log
	/etc/init.d/pcp start
	for i in 1 2 3 4 5
	do
	    sleep 1
	    pminfo -v pmcd.version >/dev/null 2>&1 && return
	done
	# some problem starting pmcd
	#
	echo "Fatal error: unable to start pmcd ..."
	if [ -f $log ]
	then
	    echo "Log file ..."
	    cat $log
	else
	    echo "No log file ($log)!!"
	fi
    fi
}

# expect -R root or $ROOT not set in environment
#
check_root()
{
    if [ "X$root" != X ]
    then
	ROOT="$root"
	export ROOT
    else
	if [ "X$ROOT" != X -a "X$ROOT" != X/ ]
	then
	    echo "Install: \$ROOT was set to \"$ROOT\""
	    echo "          Use -R rootdir to install somewhere other than /"
	    exit 1
	fi
    fi
}

# should be able to extract default domain from domain.h
#
check_domain()
{
    if [ -f domain.h ]
    then
	# $domain is for backwards compatibility, modern PMDAs
	# have something like
	#	#define FOO 123
	#
	domain=''
	eval `nawk <domain.h '
/^#define/ && $3 ~ /^[0-9][0-9]*$/	{ print $2 "=" $3
					  if (seen == 0) {
					    print "domain=" $3
					    print "SYMDOM=" $2
					    seen = 1
					  }
					}'`
	if [ "X$domain" = X ]
	then
	    echo "Install: cannot determine the Performance Metrics Domain from ./domain.h"
	    exit 1
	fi
    else
	echo "Install: cannot find ./domain.h to determine the Performance Metrics Domain"
	exit 1
    fi
}

# choose correct PMDA installation mode
#
# make sure we are installing in the correct style of configuration
#
choose_mode()
{
    def=m
    $do_pmda && def=b
    echo \
'You will need to choose an appropriate configuration for installation of
the "'$iam'" Performance Metrics Domain Agent (PMDA).

  collector	collect performance statistics on this system
  monitor	allow this system to monitor local and/or remote systems
  both		collector and monitor configuration for this system
'
    while true
    do
	echo 'Please enter c(ollector) or m(onitor) or b(oth) ['$def'] \c'
	read ans
	case "$ans"
	in
	    "")	break
		    ;;
	    c|collector|b|both)
		    do_pmda=true
		    break
		    ;;
	    m|monitor)
		    do_pmda=false
		    break
		    ;;
	    *)	echo "Sorry, that is not acceptable response ..."
		    ;;
	esac
    done
    echo
}

# choose an IPC method
#
choose_ipc()
{
    _dir=${1-/usr/pcp/lib}
    ipc_type=''
    $pipe_opt && ipc_type=pipe
    $socket_opt && ipc_type=socket
    $pipe_opt && $socket_opt && ipc_type=''
    if [ -z "$ipc_type" ]
    then
	while true
	do
	    echo "PMCD should communicate with the $iam daemon via a pipe or a socket? [pipe] \c"
	    read ipc_type
	    if  [ "X$ipc_type" = Xpipe -o "X$ipc_type" = X ]
	    then
		ipc_type=pipe
		break
	    elif [ "X$ipc_type" = Xsocket ]
	    then
		break
	    else
		echo "Must choose one of \"pipe\" or \"socket\", please try again"
	    fi
	done
    fi

    if [ $ipc_type = pipe ]
    then
	type="pipe	$ipc_prot 		$_dir/$pmda_name"
    else
	echo "Use Internet or Unix domain sockets? [Internet] \c"
	read ans
	if [ "X$ans" = XInternet -o "X$ans" = X ]
	then
	    echo "Internet port number or service name? [$socket_inet_def] \c"
	    read port
	    [ "X$port" = X ] && port=$socket_inet_def
	    case $port
	    in
		[0-9]*)
			;;
		*)
			if grep "^$port[ 	]*[0-9]*/tcp" /etc/services >/dev/null 2>&1
			then
			    :
			else
			    echo "Warning: there is no tcp service for \"$port\" in /etc/services!"
			fi
			;;
	    esac
	    type="socket	inet $port	$_dir/$pmda_name"
	    args="-i $port $args"
	    break
	elif [ "X$ans" = XUnix ]
	then
	    echo "Unix FIFO name? \c"
	    read fifo
	    if [ "X$fifo" = X ]
	    then
		echo "Must provide a name, please try again"
	    else
		type="socket	unix $fifo	$_dir/$pmda_name"
		args="-u $fifo $args"
		break
	    fi
	else
	    echo "Must choose one of \"Unix\" or \"Internet\", please try again"
	fi
    fi
}

# determine license capabilities you have, report and exit if
# no licenses are valid
#
# Note.  if someone fakes out /usr/pcp/bin/pmbrand, this test will produce
#	 the wrong result, but other things down stream will not work
#
_check_license()
{
    LICENSE_PCP=false
    LICENSE_WEBMETER=false
    [ ! -x /usr/pcp/bin/pmbrand ] && return
    rm -f /tmp/.$$.check
    /usr/pcp/bin/pmbrand -l >/tmp/.$$.check 2>&1
    fgrep -s 'PCP' </tmp/.$$.check && LICENSE_PCP=true
    fgrep -s 'WEBPERF
WebMeter' </tmp/.$$.check && LICENSE_WEBMETER=true
    rm -f /tmp/.$$.check

    if [ $LICENSE_PCP = false -a $LICENSE_WEBMETER = false ]
    then
	echo
	echo \
'There are no valid Performance Co-Pilot licenses installed on this host.
Please install a valid Collector, Monitor or WebMeter license before
attempting to install this PMDA again.'
  	echo
	exit 1
    fi

    if [ $LICENSE_PCP = false -a $LICENSE_WEBMETER = true ]
    then
    	NAMESPACE=/var/pcp/pmns/root_web
	PMNSROOT=`basename $NAMESPACE`
	PMNSDIR=`dirname $NAMESPACE`
    	if [ ! -r $NAMESPACE ]
    	then
	    echo
	    echo \
'The WebMeter license requires the WebMeter product, and hence the file
$NAMESPACE, to be installed.'
	    echo
	    exit 1
    	fi
    fi
}

# Function to filter pminfo output
#
_filter()
{
    awk '
NF==0		{ next }
/ value /	{ value++; next }
/^'$1'/		{ metric++
		  if ($0 !~ /:/) next
		}
		{ warn++ }
END		{ if (warn) printf "%d warnings, ",warn
		  printf "%d metrics and %d values\n",metric,value
		}'
}

_setup()
{
    # Set $domain and $SYMDOM from domain.h
    #
    check_domain

    case $prog
    in
	*Install*)
	    # Check that $ROOT is not set, we have a default domain value and
	    # choose the installation mode (collector, monitor or both)
	    #
	    check_root
	    choose_mode
	    ;;
    esac

    # some more configuration controls
    pmns_name=${iam}
    pmda_name=pmda${iam}
    dso_name=pmda_${iam}.so
    dso_entry=${iam}_init
}

# Configurable PMDA installation
#
# before calling _install,
# 1. set $iam
# 2. set one or both of $dso_opt or $daemon_opt true (optional, $daemon_opt
#    is true by default)
# 3. if $dso_opt set one or both of $pipe_opt or $socket_opt true (optional,
#    $pipe_opt is true by default)
# 4. if $socket_opt and there is an default Internet socket, set
#    $socket_inet_def

_install()
{
    if [ -z "$iam" ]
    then
	echo 'Botch: must define $iam before calling _install_preamble()'
	exit 1
    fi

    if $do_pmda
    then
	if $dso_opt || $daemon_opt
	then
	    :
	else
	    echo 'Botch: must set at least one of $dso_opt or $daemon_opt to "true"'
	    exit 1
	fi
	if $daemon_opt
	then
	    if $pipe_opt || $socket_opt
	    then
		:
	    else
		echo 'Botch: must set at least one of $pipe_opt or $socket_opt to "true"'
		exit 1
	    fi
	fi
    fi

    # check that we have a valid license: for WebMeter, skip the PMNS changes
    #
    _check_license

    if $LICENSE_PCP
    then
    	echo "Updating the Performance Metrics Name Space (PMNS) ..."
    	if pminfo -n $NAMESPACE $pmns_name >/dev/null 2>&1
    	then
	    cd $PMNSDIR
	    if /usr/pcp/bin/pmnsdel -n $PMNSROOT $pmns_name >$tmp 2>&1
	    then
	    	:
 	    else
	    	if grep 'Non-terminal "'"$pmns_name"'" not found' $tmp >/dev/null
	    	then
		    :
		elif grep 'Error: metricpath "'"$pmns_name"'" not defined' $tmp >/dev/null
		then
		    :
	    	else
		    echo "$prog: failed to delete \"$pmns_name\" from the PMNS"
		    cat $tmp
		    exit 1
	    	fi
	    fi
	    cd $here
    	fi
    fi

    if $LICENSE_PCP
    then
    	# Install the namespace
    	#

	# Put the default domain number into the namespace file
	#
	sed -e "s/$SYMDOM/$domain/" <$pmns_source >$PMNSDIR/$pmns_name

    	cd $PMNSDIR
    	if /usr/pcp/bin/pmnsadd -n $PMNSROOT $pmns_name
   	then
	    :
    	else
	    echo "$prog: failed to add the PMNS entries for \"$pmns_name\" ..."
	    echo
	    ls -l
	    exit 1
    	fi
    	cd $here
    elif $LICENSE_WEBMETER
    then
	if pminfo $pmns_name >$tmp.chk 2>&1
	then
	    :
	else
	    cat $tmp.chk
	    echo \
'This system has a WebMeter license which prohibits changes to the PMNS,
and the required "'$pmns_name'" metrics are not currently in the PMNS.'
	    echo
	    if $do_pmda
	    then
		echo \
'To install the "'$iam'" PMNS and PMDA on this system, you must
upgrade to a full PCP Collector license'
	    else
		echo \
'To install the "'$iam'" PMNS on this system, you must upgrade to a
full PCP Monitor license'
	    fi
	    echo
	    exit 1
	fi
    else
	echo "$prog: Warning: no valid PCP license"
	echo "PMNS not updated, and this may cause the installation to fail."
	echo
    fi
	
    if [ -d /var/pcp/config/pmchart ]
    then
	echo "Installing pmchart view(s) ..."
	for i in *.pmchart
	do
	    if [ "$i" != "*.pmchart" ]
	    then
		dest=/var/pcp/config/pmchart/`basename $i .pmchart`
		rm -f $dest
		cp $i $dest
	    fi
	done
    else
	echo "Skip installing pmchart view(s) ... no \"/var/pcp/config/pmchart\" directory"
    fi

    # Prompt for command line arguments to the PMDA
    #
    if $do_pmda
    then
	# Select an IPC method for communication between PMCD and PMDA
	#
	pmda_type=''
	$dso_opt && pmda_type=dso
	$daemon_opt && pmda_type=daemon
	$dso_opt && $daemon_opt && pmda_type=''
	if [ -z "$pmda_type" ]
        then
	    while true
	    do
		echo "Install $iam as a daemon or dso agent? [daemon] \c"
		read pmda_type
		if [ "X$pmda_type" = Xdaemon -o "X$pmda_type" = X ]
		then
		    pmda_type=daemon
		    break
		elif [ "X$pmda_type" = Xdso ]
		then
		    break
		else
		    echo "Must choose one of \"daemon\" or \"dso\", please try again"
		fi
	    done
	fi
	if [ "$pmda_type" = daemon ]
	then
	    choose_ipc /var/pcp/pmdas/$iam
	    args="-d $domain $args"
	else
	    type="dso	$dso_entry	$dso_name"
	    args=""
	fi

	# Fix domain in help for instance domains (if any)
	#
	if [ -f $help_source ]
	then
	    sed -e "/^@ $SYMDOM/s/$SYMDOM/$domain/" <$help_source \
	    | /usr/pcp/bin/newhelp -o $help_source
	fi

	# Terminate old PMDA
	#
	echo "Terminate PMDA if already installed ..."
	pmda_cull $iam $domain

	# Install binaries
	#
	echo "Installing files ..."
	if [ "$pmda_type" = dso ]
	then
	    # stolen from /etc/init.d/pcp _reboot_setup()
	    # choice depend on the binary format of the booted kernel
	    #
	    BINFMT=""
	    if [ -f /unix ]
	    then
		case "`file /unix`"
		in
		    *ELF\ 64-bit\ *)
			BINFMT=mips_64
			;;
		    *MIPSEB\ *COFF\ *|*ELF\ 32-bit\ *)
			BINFMT=mips_o32
			;;
		    *ELF\ New\ 32-bit\ *)
			BINFMT=mips_n32
		esac
	    fi
	    if [ "X$BINFMT" = X ]
	    then
		echo "$prog: Cannot determine kernel binary format from /unix"
		echo "$prog: Warning: defaulting to MIPS o32-bit binary format"
		BINFMT=mips_o32
	    fi
	    STYLE="$BINFMT"
	else
	    STYLE=""
	fi

	if make STYLE="$STYLE" install
	then
	    :
	else
	    echo "$prog: Arrgh, \"make install\" failed!"
	    exit 1
	fi

	# Add PMDA to pmcd's configuration file
	#
	echo "Updating the PMCD control file, and notifying PMCD ..."
	pmda_add "$iam	$domain	$type $args"

	# Check that the agent is running OK
	#
	if $do_check
	then
	    [ "$check_delay" -gt 5 ] && echo "Wait $check_delay seconds for the $iam agent to initialize ..."
	    sleep $check_delay
	    echo "Check $iam metrics have appeared ... \c"
	    pminfo -n $NAMESPACE -f $pmns_name | _filter $pmns_name
	fi
    else
	echo "Skipping PMDA install and PMCD re-configuration"
    fi
}

_remove()
{

    # check that we have a valid license: for WebMeter, skip the PMNS changes
    #
    _check_license

    if $LICENSE_PCP
    then
    	# Update the namespace
    	#

	echo "Culling the Performance Metrics Name Space ..."
	cd $PMNSDIR

	echo "$iam ... \c"
	if /usr/pcp/bin/pmnsdel -n $PMNSROOT $pmns_name >$tmp 2>&1
	then
	    rm -f $PMNSDIR/$pmns_name
	    echo "done"
	else
	    if grep 'Non-terminal "'"$pmns_name"'" not found' $tmp >/dev/null
	    then
		echo "not found in Name Space, this is OK"
	    elif grep 'Error: metricpath "'"$pmns_name"'" not defined' $tmp >/dev/null
	    then
		echo "not found in Name Space, this is OK"
	    else
		echo "error"
		cat $tmp
		exit
	    fi
	fi
    elif $LICENSE_WEBMETER
    then
	:
    else
	echo "$prog: Warning: no valid PCP license"
	echo "PMNS not updated, and this may cause the removal to fail."
	echo
    fi

    # Remove the PMDA and help files
    #
    cd $here

    if $do_pmda
    then
	echo "Updating the PMCD control file, and notifying PMCD ..."
	pmda_cull $iam $domain

	echo "Removing files ..."
	make clobber >/dev/null
	for i in *.pmchart
	do
	    if [ "$i" != "*.pmchart" ]
	    then
		dest=/var/pcp/config/pmchart/`basename $i .pmchart`
		rm -f $dest
	    fi
	done

	if $LICENSE_PCP
	then
	    if $do_check
	    then
		echo "Check $iam metrics have gone away ... \c"
		if pminfo -n $NAMESPACE -f $pmns_name >$tmp 2>&1
		then
		    echo "Arrgh, something has gone wrong!"
		    cat $tmp
		else
		    echo "OK"
		fi
	    fi
	fi
    else
	echo "Skipping PMDA removal and PMCD re-configuration"
    fi
}
